/**
 * Drag拖拽插件
 *      特别注意：1、当拖拽一些封装组件时，需确保其内的一些样式不会对拖拽功能造成影响（比如元素设置了transition），
 *                  否则会出现拖拽不灵敏现象；
 *              2、当未指定拖拽区域时，会默认指向body，此时需确保拖拽元素的外层祖先元素是否非static定位，
 *                  否则会出现闪动现象；
 */

import $Qilin from "qilin-utils";

export default class Drag {
    constructor(element,options){
        // 被拖拽的元素
        if(options.$el){ //若要指定拖拽元素
            if($Qilin.Element.isElement(options.$el)){
                this.$el=options.$el;
            }else if(typeof options.$el === "string" && options.$el){
                this.$el=document.querySelector(options.$el);
            };
            if(
                $Qilin.Element.getNodeCssStyle(this.$el,"position") === "static" ||
                $Qilin.Element.getNodeCssStyle(this.$el,"position") === "relative"
            ){
                this.$el.style.position=options.position || "absolute";
                this.$el.style.left=options.left || "25%";
            };
        }else{
            this.$el=element;
        };
        if(!$Qilin.Common.isObject(options)){
            options={};
        };
        // 可拖拽的区域--表示可拖拽元素的父容器元素或者其选择器名称
        this.$container=options.container;
        // x轴方向是否可拖动
        this.draggableX=options.draggableX;
        // y轴方向是否可拖动
        this.draggableY=options.draggableY;
        // 该值为on时元素的中心可以在容器边界上，值为in时元素全部只能在容器内，值为out时元素可以在容器外部边缘
        this.mode=options.mode;
        // 元素被拖动时触发
        this.drag=options.drag;
        // 元素开始拖动时触发
        this.beforeDrag=options.beforeDrag;
        // 元素拖动结束触发
        this.dragged=options.dragged;
        // 初始化完毕的回调
        this.ready=options.ready;
        // 是否可拖动
        this.draggable=false;
        // 是否已初始化
        this.hasInit=false;
        // x坐标
        this.pageX=0;
        // y坐标
        this.pageY=0;
        // 生成唯一标识
        this.guid=this._createGuid();
    };
    // 初始化
    init(){
        if(!this.hasInit){
            this.hasInit=true;
            if(!$Qilin.Element.isElement(this.$el)){
                throw new TypeError("该被拖动的元素并不是一个节点元素");
            };
            if(typeof this.$container === "string" && this.$container){
                this.$container=document.body.querySelector(this.$container);
            };
            if(!$Qilin.Element.isElement(this.$container)){
                this.$container=document.body;
            };
            if(!$Qilin.Element.isContains(this.$container,this.$el)){
                throw new TypeError("可拖拽区域元素并不是被拖拽元素的父容器元素");
            }else if($Qilin.Element.getNodeCssStyle(this.$container,"position") === "static"){
                throw new TypeError("可拖拽区域元素不能是static定位");
            }else if(
                $Qilin.Element.getNodeCssStyle(this.$el,"position") === "static" ||
                $Qilin.Element.getNodeCssStyle(this.$el,"position") === "relative"
            ){
                throw new TypeError("被拖拽元素不能是static或者relative定位");
            };
            if(typeof this.draggableX !== "boolean"){
                this.draggableX=true;
            };
            if(typeof this.draggableY !== "boolean"){
                this.draggableY=true;
            };
            if(this.mode !== "on" && this.mode !== "in" && this.mode !== "over" && this.mode !== "out"){
                this.mode="" ; //默认对拖拽范围不做限定
            };
            if(typeof this.drag !== "function"){
                this.drag =function(){};
            };
            if(typeof this.beforeDrag !== "function"){
                this.beforeDrag =function(){};
            };
            if(typeof this.dragged !== "function"){
                this.dragged =function(){};
            };
            if(typeof this.ready !== "function"){
                this.ready =function(){};
            };
            // 设置拖拽事件
            this._setOn();
            // 初始化回调
            this.ready(this);
        };
    };
    // 设置拖拽事件
    _setOn(){
        // 触摸开始
        $Qilin.Event.on(this.$el,"touchstart.drag",(e)=>{
            if(!this.draggableX && !this.draggableY){
                return;
            };
            this.pageX=e.targetTouches[0].pageX-$Qilin.Element.getNodeDistance(this.$el,this.$container).left;
            this.pageY=e.targetTouches[0].pageY-$Qilin.Element.getNodeDistance(this.$el,this.$container).top;
            this.draggable=true;
            this.$el.style.cursor="move";
            // 监听事件，监听刚开始拖动触发
            this.beforeDrag({
                target:this.$el,
                container:this.$container,
                placement:$Qilin.Element.getNodeDistance(this.$el,this.$container)
            });
        });
        // 触摸移动
        $Qilin.Event.on(this.$el,"touchmove.drag",(e)=>{
            if(this.draggable){
                if(!this.draggableX && !this.draggableY){
                    return;
                };
                if(e.cancelable){
                    e.preventDefault();
                };
                let left=e.targetTouches[0].pageX-this.pageX;
                let top=e.targetTouches[0].pageY-this.pageY;
                if(this.draggableX){
                    this.$el.style.left=left+"px";
                };
                if(this.draggableY){
                    this.$el.style.top=top+"px";
                };
                this._resize();
                if(this.draggableX || this.draggableY){
                    // 监听事件
                    this.drag({
                        target:this.$el,
                        container:this.$container,
                        placement:$Qilin.Element.getNodeDistance(this.$el,this.$container)
                    });
                };
            };
        });
        // 触摸松开后，拖拽状态更改为false，触发监听事件
        $Qilin.Event.on(this.$el,"touchend.drag",(e)=>{
            if(this.draggable){
                if(!this.draggableX && !this.draggableY){
                    return;
                };
                this.draggable=false;
                this.$el.style.cursor="";
                // 监听事件
                this.dragged({
                    target:this.$el,
                    container:this.$container,
                    placement:$Qilin.Element.getNodeDistance(this.$el,this.$container)
                });
            };
        });
        // 鼠标按下
        $Qilin.Event.on(this.$el,"mousedown.drag",(e)=>{
            if(!this.draggableX && !this.draggableY){
                return;
            };
            this.pageX=e.pageX-$Qilin.Element.getNodeDistance(this.$el,this.$container).left;
            this.pageY=e.pageY-$Qilin.Element.getNodeDistance(this.$el,this.$container).top;
            this.draggable=true;
            this.$el.style.cursor="move";
            // 监听事件，监听刚开始拖动触发
            this.beforeDrag({
                target:this.$el,
                container:this.$container,
                placement:$Qilin.Element.getNodeDistance(this.$el,this.$container)
            });
        });
        // 鼠标移动
        $Qilin.Event.on(document.body,`mousemove.drag_${this.guid}`,(e)=>{
            if(this.draggable){
                if(!this.draggableX && !this.draggableY){
                    return;
                };
                let left=e.pageX-this.pageX;
                let top=e.pageY-this.pageY;
                // 防止拖拽元素的marginTop值影响拖拽元素的top定位引发闪动现象
                if($Qilin.Element.getNodeCssStyle(this.$el,"margin-top") !== "0px"){
                    this.$el.style.marginTop="0px";
                };
                if(this.draggableX){
                    this.$el.style.left=left+"px";
                };
                if(this.draggableY){
                    this.$el.style.top=top+"px";
                };
                this._resize();
                if(this.draggableX || this.draggableY){
                    // 监听事件
                    this.drag({
                        target:this.$el,
                        container:this.$container,
                        placement:$Qilin.Element.getNodeDistance(this.$el,this.$container)
                    });
                };
            };
        });
        // 鼠标松开后，拖拽状态更改为false，触发监听事件
        $Qilin.Event.on(document.body,`mouseup.drag_${this.guid}`,(e)=>{
            if(this.draggable){
                if(!this.draggableX && !this.draggableY){
                    return;
                };
                this.draggable=false;
                this.$el.style.cursor="";
                // 监听事件
                this.dragged({
                    target:this.$el,
                    container:this.$container,
                    placement:$Qilin.Element.getNodeDistance(this.$el,this.$container)
                });
            };
        });
    };
    // 移除该指令绑定在body上的事件
    _setOff(){
        $Qilin.Event.off(document.body,`mousemove.drag_${this.guid},mouseup.drag_${this.guid}`);
    };
    // 元素超出容器范围设置
    _resize(){
        if(this.mode === "in"){
            if(this.draggableX){
                // 若往左拖拽超出容器时
                if($Qilin.Element.getNodeDistance(this.$el,this.$container).left <= 0){
                    this.$el.style.left=0;
                };
                // 若往右拖拽超出容器时
                if($Qilin.Element.getNodeDistance(this.$el,this.$container).left >= this.$container.offsetWidth-this.$el.offsetWidth){
                    this.$el.style.left=this.$container.offsetWidth-this.$el.offsetWidth+"px";
                };
            };
            if(this.draggableY){
                // 若往上拖拽超出容器时
                if($Qilin.Element.getNodeDistance(this.$el,this.$container).top <= 0){
                    this.$el.style.top=0;
                };
                // 若往下拖拽超出容器时
                if($Qilin.Element.getNodeDistance(this.$el,this.$container).top >= this.$container.offsetHeight-this.$el.offsetHeight){
                    this.$el.style.top=this.$container.offsetHeight-this.$el.offsetHeight+"px";
                };
            };
        }else if(this.mode === "on"){
            if(this.draggableX){
                // 若往左拖拽至己身一半超出容器时
                if($Qilin.Element.getNodeDistance(this.$el,this.$container).left <= -this.$el.offsetWidth/2){
                    this.$el.style.left=-this.$el.offsetWidth/2+"px";
                };
                // 若往右拖拽至己身一半超出容器时
                if($Qilin.Element.getNodeDistance(this.$el,this.$container).left >= this.$container.offsetWidth-this.$el.offsetWidth/2){
                    this.$el.style.left=this.$container.offsetWidth-this.$el.offsetWidth/2+"px";
                };
            };
            if(this.draggableY){
                // 若往上拖拽至己身一半超出容器时
                if($Qilin.Element.getNodeDistance(this.$el,this.$container).top <= -this.$el.offsetHeight/2){
                    this.$el.style.top=-this.$el.offsetHeight/2+"px";
                };
                // 若往下拖拽至己身一半超出容器时
                if($Qilin.Element.getNodeDistance(this.$el,this.$container).top >= this.$container.offsetHeight-this.$el.offsetHeight/2){
                    this.$el.style.top=this.$container.offsetHeight-this.$el.offsetHeight/2+"px";
                };
            };
        }else if(this.mode === "out"){
            if(this.draggableX){
                // 若往左拖拽至己身超出容器时
                if($Qilin.Element.getNodeDistance(this.$el,this.$container).left <= -this.$el.offsetWidth){
                    this.$el.style.left=-this.$el.offsetWidth+"px";
                };
                // 若往右拖拽至己身超出容器时
                if($Qilin.Element.getNodeDistance(this.$el,this.$container).left >= this.$container.offsetWidth){
                    this.$el.style.left=this.$container.offsetWidth+"px";
                };
            };
            if(this.draggableY){
                // 若往上拖拽至己身超出容器时
                if($Qilin.Element.getNodeDistance(this.$el,this.$container).top <= -this.$el.offsetHeight){
                    this.$el.style.top=-this.$el.offsetHeight+"px";
                };
                // 若往下拖拽至己身超出容器时
                if($Qilin.Element.getNodeDistance(this.$el,this.$container).top >= this.$container.offsetHeight){
                    this.$el.style.top=this.$container.offsetHeight+"px";
                };
            };
        };
    };
    // 生成唯一值
    _createGuid(){
        // 获取当前guid，不存在则从0开始
        let guid=$Qilin.Data.getNodeData(document.body,"qilin-directives-drag-guid") || 0;
        guid++;
        $Qilin.Data.setNodeData(document.body,"qilin-directives-drag-guid",guid);
        return guid;
    };
    // 移动元素到指定位置
    dragTo(left,top){
        return new Promise((resolve,reject)=>{
            this.beforeDrag({
                target:this.$el,
                container:this.$container,
                placement:$Qilin.Element.getNodeDistance(this.$el,this.$container)
            });
            if(this.draggableX){
                this.$el.style.left=left+"px";
            };
            if(this.draggableY){
                this.$el.style.top=top+"px";
            };
            this._resize();
            let options={
                target:this.$el,
                container:this.$container,
                placement:$Qilin.Element.getNodeDistance(this.$el,this.$container)
            };
            this.dragged(options);
            resolve(options);
        });
    };
};